package chargemanager

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"io"
	"time"

	"dev.ixmxm.top/rms/utils/log"
)

type ChargerCode uint8

const (
	ChargerOk                 ChargerCode = 0 //正常
	ChargerChargeError        ChargerCode = 1 //充电失败
	ChargerStartChargeError   ChargerCode = 2 //机械臂伸出异常
	ChargerFinishChargeError  ChargerCode = 3 //机械臂缩回异常
	ChargerCommunicationError ChargerCode = 4 //充电桩通讯异常
)

//状态
type ChargerStatusWord struct {
	frameHead    uint16      //包头
	ID           uint8       //充电桩编号
	IsOut        bool        //已伸出
	IsIn         bool        //已缩回
	IsOuting     bool        //正在伸出
	IsRetracting bool        //正在缩回
	IsCharging   bool        //正在充电
	Current      uint8       //电流
	Code         ChargerCode //故障码
	crc16        uint16      //校验码
	frameTail    uint16      //包尾
	lastCommTime time.Time   //上次连接时间
}

func (word *ChargerStatusWord) Write(in []byte) (int, error) {
	reader := bytes.NewBuffer(in)
	binary.Read(reader, binary.BigEndian, &word.frameHead)
	binary.Read(reader, binary.BigEndian, &word.ID)
	var status byte
	binary.Read(reader, binary.BigEndian, &status)
	word.parseStatus(status)
	binary.Read(reader, binary.BigEndian, &word.Current)
	binary.Read(reader, binary.BigEndian, &word.Code)
	binary.Read(reader, binary.BigEndian, &word.crc16)
	binary.Read(reader, binary.BigEndian, &word.frameTail)
	return len(in), nil
}

func (word *ChargerStatusWord) parseStatus(status byte) {
	bits := bytesToBits([]byte{status})
	word.IsOut = bits[7] == 1
	word.IsIn = bits[6] == 1
	word.IsOuting = bits[5] == 1
	word.IsRetracting = bits[4] == 1
	word.IsCharging = bits[3] == 1
}

func (word *ChargerStatusWord) splitFunc() bufio.SplitFunc {
	return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		//eof or the package is less then head length
		if atEOF {
			return 0, nil, io.EOF
		}
		if len(data) < lenHead {
			return 0, nil, nil
		}
		process := data
		//if not start with the head
		if !bytes.Equal(data[0:lenHead], frameHeader[:]) {
			//try to find the next head
			index := bytes.Index(data, frameHeader[:])
			if index != -1 {
				//find the next head
				process = data[index:]
			} else {
				//can not find the next head,so throw all the data
				return len(data), nil, nil
			}
		}
		if len(process) < totalLen {
			//if process data is less then the total length,wait for the new bytes
			return 0, nil, nil
		}
		pack := process[:totalLen]
		//check sum
		if binary.BigEndian.Uint16(pack[6:8]) != getCrc16(pack[2:6]) {
			//the sum is not equal,throw the package
			log.Warn("package sum not match")
			return totalLen, nil, nil
		}
		return totalLen, pack, nil
	}
}

func (word *ChargerStatusWord) expired() bool {
	return time.Now().Sub(word.lastCommTime) > 3*time.Second
}
